﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

/** 
 * The Animation class is used to draw a game sprite to the screen. It uses a series of frames
 * and a specific frame rate to determine what should be drawn to the screen at a particular moment.
 */

public class Animation
{
    public String name;
    
    /**
     * If an animation is not loopable, then it will rest on the final frame of the animation.
     */
    public Boolean loopable;
    
    public ArrayList frames;
    
    /** 
     * Frame rate of the animation
     */
    public int frameRate = 12;
    
    /** 
     * Used in determining how many seconds to wait per frame.
     */
    public double millisecondsPerFrame;

    /* 
     * holds a value that shows how much time has passed since the last update() call. Used in determining the current frame.
     */
    public double timeSinceLastFrame;
    
    /**
     * the current frame number of the animation
     */
    private int currentFrameNum;
    
    /**
     * this is the position of the Animation on screen
     */
    private Vector2 origin;

    public Boolean isFinished = false;

    private SpriteEffects spriteEffects;

    /**
     * Construstor
     */

    public Animation(String name, int frameRate, Boolean loopable)
    {
        init();
        this.name = name;
        setFrameRate(frameRate);
        this.origin = new Vector2(0,0);
        this.loopable = loopable;
        setMillisecondsPerFrameRate();
    }
    
    public Animation(String name, int frameRate, Boolean loopable, ArrayList frames)
    {
        init();
        this.name = name;
        setFrameRate(frameRate);
        this.origin = new Vector2(0,0);
        this.frames = frames;
        this.loopable = loopable;
        setMillisecondsPerFrameRate();
    }
    
    

    private void init()
    {
        frames = new ArrayList();
        loopable = true;
        currentFrameNum = 0;
        timeSinceLastFrame = 0;
        spriteEffects = SpriteEffects.None;
    }

    public void setCurrentFrame(int num)
    {
        if (num < frames.Count && num >= 0)
        {
            currentFrameNum = num;
        }
    }

    /**
     * This method figures out how many milliseconds are in a frame given the framerate.
     */
    private void setMillisecondsPerFrameRate()
    {
        if (frameRate == 0)
        {
            return;
        }
        millisecondsPerFrame = 1000 / frameRate;
    }

    /** 
     * adds a frame to the ArrayList of frames
     */
    public void addFrame(Frame frame)
    {
        frames.Add(frame);
    }
    /** 
     * Sets the loopable property of this animation
     */
    public void setLoopable(Boolean loopable)
    {
        this.loopable = loopable;
    }

    /** 
     * Get the current frame number of this animation
     */
    public int getFrameNumber()
    {
        return currentFrameNum;
    }

    /** 
     * Gets the total frames of this animation
     */
    public int getTotalFrames()
    {
        return frames.Count;
    }

    /**
     * Selects the next frame based on the number of frames that may have passed since the last update call. If
     * this animation is not loopable, then it will display the final frame once it has been reached.
     */
    private void gotoNextFrame(double timeElapsed)
    {
        if (frameRate > 0)
        {
            double frameDelay = (((Frame)frames[currentFrameNum]).waitTime + 1) * millisecondsPerFrame;
            timeSinceLastFrame += timeElapsed;
            while (timeSinceLastFrame > frameDelay)
            {
                //timeSinceLastFrame = 0;
                timeSinceLastFrame -= frameDelay;
                currentFrameNum++;
                if (currentFrameNum >= frames.Count)
                {
                    if (loopable)
                    {
                        // loop the animation
                        currentFrameNum = 0;
                    }
                    else
                    {
                        // hold on the final frame
                        currentFrameNum = frames.Count - 1;
                        isFinished = true;
                    }
                }
                frameDelay = (((Frame)frames[currentFrameNum]).waitTime + 1) * millisecondsPerFrame;
            }
        }
    }
    

    public void setFrameRate(int framerate){
        if(framerate < 0){
            frameRate = 0;
        }  else {
            frameRate = framerate;
        }
        setMillisecondsPerFrameRate();
    }

    public void setEffects(SpriteEffects spriteEffects)
    {
        this.spriteEffects = spriteEffects;
    }

    /**
     * The update method finds the next frame of the animation and sets the origin of the current frame to 
     * the origin of the Animtion
     */
    public void update(double timeElapsed)
    {
        gotoNextFrame(timeElapsed);
        //((Frame)frames[currentFrameNum]).origin = this.origin;
    }

    public void render(Vector2 position)
    {
        render(position, new Color(1.0f, 1.0f, 1.0f), spriteEffects, 0.0f);
    }

    public void render(Vector2 position, float depth)
    {
        render(position, new Color(1.0f, 1.0f, 1.0f), spriteEffects, depth);
    }

    public void render(Vector2 position, float depth, float rotation)
    {
        render(position, new Color(1.0f, 1.0f, 1.0f), spriteEffects, depth, rotation);
    }

    public void render(Vector2 position, float depth, Vector2 scale)
    {
        render(position, new Color(1.0f, 1.0f, 1.0f), spriteEffects, depth, 0.0f, scale);
    }

    public void render(Vector2 position, SpriteEffects effects, float depth)
    {
        setEffects(effects);
        render(position, new Color(1.0f, 1.0f, 1.0f), spriteEffects, depth);
    }

    public void render(Vector2 position, Color color, SpriteEffects effects, float depth)
    {
        setEffects(effects);
        Frame currentFrame = (Frame)frames[currentFrameNum];
        Game.getGame().getSpriteBatch().Draw(
            currentFrame.asset.spriteSheet, // Texture2D
            position, // Vector 2
            currentFrame.sourceRectangle, // Rectangle
            color, // color
            0.0f, // rotation float
            currentFrame.origin, // vector2D 
            1.0f, // scale float
            spriteEffects, // SpriteEffect
            depth); // float
    }

    public void render(Vector2 position, Color color, SpriteEffects effects, float depth, float rotation)
    {
        setEffects(effects);
        Frame currentFrame = (Frame)frames[currentFrameNum];
        Game.getGame().getSpriteBatch().Draw(
            currentFrame.asset.spriteSheet, // Texture2D
            position, // Vector 2
            currentFrame.sourceRectangle, // Rectangle
            color, // color
            rotation, // rotation float
            currentFrame.origin, // vector2D 
            1.0f, // scale float
            spriteEffects, // SpriteEffect
            depth); // float
    }

    public void render(Vector2 position, Color color, SpriteEffects effects, float depth, float rotation, Vector2 scale)
    {
        setEffects(effects);
        Frame currentFrame = (Frame)frames[currentFrameNum];
        Game.getGame().getSpriteBatch().Draw(
            currentFrame.asset.spriteSheet, // Texture2D
            position, // Vector 2
            currentFrame.sourceRectangle, // Rectangle
            color, // color
            rotation, // rotation float
            currentFrame.origin, // vector2D 
            scale, // scale float
            spriteEffects, // SpriteEffect
            depth); // float
    }

}
